

#include "lua_pcall.h"

/********************************************************************************/

static int finishpcall(lua_State *L, int status, lua_KContext extra) {
  if (luai_unlikely(status != LUA_OK && status != LUA_YIELD)) {  /* error? */
    lua_pushboolean(L, 0);  /* first result (false) */
    lua_pushvalue(L, -2);  /* error message */
    return 2;  /* return false, msg */
  }
  return lua_gettop(L) - (int)extra;  /* return all results */
}

static int luac_xpcall(lua_State *L) {
  int status;
  int n = lua_gettop(L);
  luaL_checktype(L, 2, LUA_TFUNCTION);  /* check error function */
  lua_pushboolean(L, 1);  /* first result */
  lua_pushvalue(L, 1);  /* function */
  lua_rotate(L, 3, 2);  /* move them below function's arguments */
  status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
  return finishpcall(L, status, 2);
}

static int luac_pcall(lua_State* L) {
  lua_pushcfunction(L, traceback);
  lua_insert(L, 2);
  return luac_xpcall(L);
}

/********************************************************************************/

LUAOS_API int lua_xpcall(lua_State* L, int n, int r, int f) {
  int ef = f;
  if (ef == 0) {
    ef = lua_gettop(L) - n;
    lua_pushcfunction(L, traceback);
    lua_insert(L, ef);
  }
  int status = lua_pcallk(L, n, r, ef, 0, 0);
  if (f == 0) {
    lua_remove(L, ef); /* remove traceback from stack */
  }
  return status;
}

LUAOS_API int luaopen_pcall(lua_State* L) {
  static const luaL_Reg methods[] = {
    { "pcall",    luac_pcall    }, /* pcall (f [, arg1, ...]) */
    { "xpcall",   luac_xpcall   }, /* xpcall(f, msgh [, arg1, ...]) */
    { NULL,       NULL          }
  };
  return new_module(L, LUA_GNAME, methods);
}

/********************************************************************************/
